'use strict';

const BaseService = require('./base');
const xml2js = require('xml2js');
const fse = require('fs-extra');
const { exec } = require('child_process');
const modelSort = require('./modelSort');
const modelCal = require('./modelCalculate');

class Block {
  constructor(id, inputs, outputs) {
    this.id = id;
    this.inputs = inputs;
    this.outputs = outputs;
  }
}

function getModelIndexById(id, nodes) {
  for (let i = 0; i < nodes.length; i++) {
    const element = nodes[i];
    if (element.id == id) {
      return i;
    }
  }
  return -1;
}

function isDelayModel(id, nodes) {
  for (let i = 0; i < nodes.length; i++) {
    const element = nodes[i];
    if (element.id == id && element.name == "delay") {
      return true;
    }
  }
  return false;
}

function getInOutInfo(id, data, isIgnoreDelay) {
  let inputList = [];
  let outputList = [];
  for (let i = 0; i < data.edges.length; i++) {
    const line = data.edges[i];
    if (line.sourceNode == id) {
      if (isIgnoreDelay && isDelayModel(id, data.nodes)) {
        continue;
      }
      outputList.push(getModelIndexById(line.targetNode, data.nodes));
    } else if (line.targetNode == id) {
      if (isIgnoreDelay && isDelayModel(id, data.nodes)) {
        continue;
      }
      inputList.push(getModelIndexById(line.sourceNode, data.nodes));
    }
  }
  return { input: inputList, output: outputList };
}

class ExampleService extends BaseService {
  async startRun(step_size, mockData) {
    let ret = {
      code: 0,
      sortInfo: []
    }

    // 重置日志
    this.app.logger.reload();

    // 1. 画板转换
    let blocksWithCycle = [];
    for (let i = 0; i < mockData.nodes.length; i++) {
      const element = mockData.nodes[i];
      if (element.fctName == "delay") {
        blocksWithCycle.push(new Block(i, [], []));
        continue;
      }
      let info = getInOutInfo(element.id, mockData, true);
      blocksWithCycle.push(new Block(i, info.input, info.output));
    }

    // 2. 是否存在环
    if (modelSort.detectCycleDFS(blocksWithCycle)) {
      ret.code = 1;
      return ret;
    }

    // 3. 排序
    let sortStr = '';
    let logHead = '时间(s)';
    let dispCount = 0;
    let sortList = modelSort.topologicalSort(blocksWithCycle);
    let sortInfo = [];
    for (let i = 0; i < sortList.length; i++) {
      const idx = sortList[i];
      sortInfo.push({
        idx: idx,
        fctName: mockData.nodes[idx].fctName
      });
      sortStr = sortStr + mockData.nodes[idx].name + '->';
      if (mockData.nodes[idx].fctName == 'disp') {
        logHead = logHead + ',disp' + dispCount;
        dispCount++;
      }
    }
    modelCal.global.init_log_title = logHead;
    sortStr = sortStr + 'end';
    console.log('排序：', sortStr);

    // 4. 输入输出映射绑定
    for (let i = 0; i < mockData.nodes.length; i++) {
      const element = mockData.nodes[i];
      let info = getInOutInfo(element.id, mockData, false);
      // 记录模块的输入与输出映射
      mockData.nodes[i].inputs = info.input;
      if (element.fctName == "delay") {
        mockData.nodes[i].outvalue = element.params[0].value;
      } else {
        mockData.nodes[i].outvalue = 0;
      }
    }

    // 5. 初始化仿真数据
    modelCal.global.steps = 0;
    modelCal.global.step_size = step_size;
    modelCal.global.nodes = mockData.nodes;

    ret.code = 0;
    ret.sortInfo = sortInfo;
    return ret;
  }

  async stepRun(sample, sortInfo) {
    if (modelCal.global.steps == 0) {
      this.app.logger.write(modelCal.global.init_log_title);
    }

    let lastDesps = [];
    for (let s = 0; s < sample; s++) {
      let dispStr = '';
      // 执行一次计算
      for (let i = 0; i < sortInfo.length; i++) {
        const item = sortInfo[i];
        modelCal.global.idx = item.idx;
        let out = modelCal[item.fctName]();
        modelCal.global.nodes[item.idx].outvalue = out;  // 每次模块的输出值记录下来

        // 记录显示模块的输出
        if (item.fctName == 'disp') {
          dispStr = dispStr + ',' + out;
          // 最后一次返回前端
          if (s >= sample - 1) {
            lastDesps.push(modelCal.global.nodes[item.idx]);
          }
        }
      }
      if (dispStr.length > 0) {
        let curTime = (modelCal.global.steps * modelCal.global.step_size).toFixed(6);
        this.app.logger.write(`${curTime}${dispStr}`);
      }

      // 当前步数
      modelCal.global.steps++;
    }
    return lastDesps;
  }
}

module.exports = ExampleService;